/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.inputmethod.pinyin;

import android.content.Context;
import android.content.res.Resources;
import android.inputmethodservice.InputMethodService;
import android.os.Handler;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputConnection;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.ViewFlipper;

import java.util.ArrayList;
import java.util.List;

import static com.android.inputmethod.pinyin.PinyinIME.TAG;

/**
 * The top container to host soft keyboard view(s).
 */
public class SkbContainer extends RelativeLayout implements OnTouchListener{
    /**
     * For finger touch, user tends to press the bottom part of the target icon_setting,
     * or he/she even presses the area out of it, so it is necessary to make a
     * simple bias correction. If the input method runs on emulator, no bias
     * correction will be used.
     */
    private static final int Y_BIAS_CORRECTION = -10;

    /**
     * Used to skip these move events whose position is too close to the
     * previous touch events.
     */
    private static final int MOVE_TOLERANCE = 6;

    /**
     * If this member is true, PopupWindow is used to show on-icon_setting highlight
     * effect.
     */
    private static boolean POPUPWINDOW_FOR_PRESSED_UI = false;

    /**
     * The current soft keyboard layout.
     * 
     * @see com.android.inputmethod.pinyin.InputModeSwitcher for detailed layout
     *      definitions.
     */
    private int mSkbLayout = 0;

    /**
     * The input method service.
     */
    public PinyinIME mService;

    /**
     * Input mode switcher used to switch between different modes like Chinese,
     * English, etc.
     */
    private InputModeSwitcher mInputModeSwitcher;

    /**
     * The gesture detector.
     */
    private GestureDetector mGestureDetector;

    private Environment mEnvironment;


    /**
     * The popup balloon hint for icon_setting press/release.
     */
    private BalloonHint mBalloonPopup;

    /**
     * The on-icon_setting balloon hint for icon_setting press/release.
     */
    private BalloonHint mBalloonOnKey = null;


    /**
     * The last parameter when function {@link #toggleCandidateMode(boolean)}
     * was called.
     */
    private boolean mLastCandidatesShowing;

    /** Used to indicate whether a popup soft keyboard is shown. */
    private boolean mPopupSkbShow = false;

    private int mPopupX;

    private int mPopupY;

    /**
     * When user presses a icon_setting, a timer is started, when it times out, it is
     * necessary to detect whether user still holds the icon_setting.
     */
    private volatile boolean mWaitForTouchUp = false;
    private volatile boolean mTimerRuning = false;

    /**
     * When user drags on the soft keyboard and the distance is enough, this
     * drag will be recognized as a gesture and a gesture-based action will be
     * taken, in this situation, ignore the consequent events.
     */
    private volatile boolean mDiscardEvent = false;




    public SkbContainer(Context context, AttributeSet attrs) {
        super(context, attrs);

        mEnvironment = Environment.getInstance();


        // If it runs on an emulator, no bias correction


        mBalloonPopup = new BalloonHint(context, this, MeasureSpec.AT_MOST);
        if (POPUPWINDOW_FOR_PRESSED_UI) {
            mBalloonOnKey = new BalloonHint(context, this, MeasureSpec.AT_MOST);
        }

    }

    public void setService(PinyinIME service) {
        mService = service;
    }

    public void setInputModeSwitcher(InputModeSwitcher inputModeSwitcher) {
        mInputModeSwitcher = inputModeSwitcher;
    }

    public void setGestureDetector(GestureDetector gestureDetector) {
        mGestureDetector = gestureDetector;
    }


    public void toggleCandidateMode(boolean candidatesShowing) {
        mLastCandidatesShowing = candidatesShowing;

    }

    public void updateInputMode() {
        int skbLayout = mInputModeSwitcher.getSkbLayout();
        if (mSkbLayout != skbLayout) {
            mSkbLayout = skbLayout;
            updateSkbLayout();
        }

        mLastCandidatesShowing = false;
        invalidate();
        return;
    }

    public ListView pinyinListView = null;
    private void updateSkbLayout() {

        removeAllViews();
        View keyBorad = LayoutInflater.from(getContext()).inflate(mSkbLayout,this);
        ArrayList<View> views = new ArrayList<View>();
        keyBorad.findViewsWithText(views,"KeyBoard",FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
        for(View v:views)
        {
            v.setOnTouchListener(this);
        }

        pinyinListView = (ListView) keyBorad.findViewWithTag("pinyinListView");
        addListenerForPinyinListView(pinyinListView);
    }
    public void updateSkbLayout(int resId) {
        removeAllViews();
        LayoutInflater.from(getContext()).inflate(resId,this);
    }

    private void addListenerForPinyinListView(ListView listView)
    {
        if(listView == null) return;
        listView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                Log.i(TAG, "onItemSelected: "+position+view.toString());
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });
    }

    private void responseKeyEvent(View sKey) {
        if (null == sKey) return;
        ((PinyinIME) mService).responseSoftKeyEvent(sKey);
        return;
    }


    private void resetKeyPress(long delay) {


    }


    public void dismissPopups() {
        resetKeyPress(0);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Environment env = Environment.getInstance();
        int measuredWidth = env.getScreenWidth();
        int measuredHeight = getPaddingTop();
        measuredHeight += env.getSkbHeight();
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth,
                MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(measuredHeight,
                MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    LongPressTimer timer;
    private boolean processDelKeyLongPress(View v,MotionEvent event)
    {
        if(!(v instanceof NineKeyButton)) return false;

        if(timer == null)
        {
            timer = new LongPressTimer(this);
        }
        timer.setLongPressView(v);
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                timer.setSwiftMode(LongPressTimer.SWIFT_CENTER);
                timer.startTimer();
                break;
            case MotionEvent.ACTION_UP:
                return timer.removeTimer();
            case MotionEvent.ACTION_MOVE:
                float x = event.getX();
                float y = event.getY();
                if(x < 0)   //Left
                {
                    if(y < 0)   //left top
                    {
                        if(Math.abs(y)-Math.abs(x) > 0)
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_TOP);
                        }else
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_LEFT);
                        }
                        return true;
                    }else if(y > v.getHeight())  //left bottom
                    {
                        if(Math.abs(y-v.getHeight())-Math.abs(x) > 0)
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_BOTTOM);
                        }else
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_LEFT);
                        }
                        return true;
                    }else
                    {
                        timer.setSwiftMode(LongPressTimer.SWIFT_LEFT);
                        return true;
                    }
                }else if(x > v.getWidth())  //right
                {
                    if(y < 0)   //right top
                    {
                        if(Math.abs(y)-Math.abs(x-v.getWidth()) > 0)
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_TOP);
                        }else
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_RIGHT);
                        }
                        return true;
                    }else if(y > v.getHeight())  //right bottom
                    {
                        if(Math.abs(y-v.getHeight())-Math.abs(x-v.getWidth()) > 0)
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_BOTTOM);
                        }else
                        {
                            timer.setSwiftMode(LongPressTimer.SWIFT_RIGHT);
                        }
                        return true;
                    }else
                    {
                        timer.setSwiftMode(LongPressTimer.SWIFT_RIGHT);
                        return true;
                    }
                }else //center
                {
                    if(y < 0)   //center top
                    {
                        timer.setSwiftMode(LongPressTimer.SWIFT_TOP);
                    }else if(y > v.getHeight())  //center bottom
                    {
                        timer.setSwiftMode(LongPressTimer.SWIFT_BOTTOM);
                    }else
                    {
                        timer.setSwiftMode(LongPressTimer.SWIFT_CENTER);
                    }
                    return mTimerRuning;
                }
            case MotionEvent.ACTION_CANCEL:
                timer.removeTimer();
                return true;
            default:
                break;
        }
        return false;
    }

    boolean isSwift = true;

    int buttonId;
    float lastX,lastY;
    float delta = 10;
    public boolean onTouch(View v, MotionEvent event) {
        Log.i(TAG, "onTouch: "+v.toString());
        if(processDelKeyLongPress(v,event)) return false;
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                buttonId = v.getId();
                lastX = event.getRawX();
                lastY = event.getRawY();
                SoundManager.getInstance(getContext(),mService.getApplication()).playKeyDown();
                break;
            case MotionEvent.ACTION_UP:
                delta = v.getWidth()>v.getHeight()?v.getWidth():v.getHeight();
                delta /= 2;
                if(buttonId == v.getId() && Math.abs(lastX-event.getRawX())<delta
                        &&Math.abs(lastY-event.getRawY())<delta)
                {
                    responseKeyEvent(v);
                    if(v.getTag() != null && v.getTag().equals("KEY_CAPS"))
                        v.setActivated(InputModeSwitcher.KeyBoards.CAPS);
                    Log.i(TAG, "onTouch: "+v.getTag());
                }
                break;
            case MotionEvent.ACTION_MOVE:
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return false;
    }

    class LongPressTimer extends Handler implements Runnable {

        public static final int LONG_PRESS_TIMEOUT1 = 500;

        private static final int LONG_PRESS_TIMEOUT2 = 100;

        SkbContainer mSkbContainer;

        NineKeyButton longPressView =null;

        int mSwiftMode = 0;

        public LongPressTimer(SkbContainer skbContainer) {
            mSkbContainer = skbContainer;
        }

        public void setLongPressView(View v)
        {
            longPressView = (NineKeyButton) v;
        }

        public void setSwiftMode(int swiftMode)
        {
            if(mSwiftMode == swiftMode) return;
            mSwiftMode = swiftMode;
            createPopForKey(longPressView,swiftMode,isSwift);
        }

        public void startTimer() {
            mWaitForTouchUp = true;
            postAtTime(this, SystemClock.uptimeMillis() + LONG_PRESS_TIMEOUT1);
        }

        public boolean removeTimer() {
            isSwift = true;
            boolean isBallShown = false;
            mWaitForTouchUp = false;
            mTimerRuning = false;
            mSwiftMode = SWIFT_CENTER;
            removeCallbacks(this);
            if(mSkbContainer.mBalloonPopup.isShowing())
            {
                mSkbContainer.mService.chooseCandidate(-1);
                mSkbContainer.mService.commitResultText(mSkbContainer.mBalloonPopup.mLable);
                mSkbContainer.mBalloonPopup.dismiss();
                isBallShown = true;
            }
            return isBallShown;
        }

        final static int SWIFT_CENTER = 0;
        final static int SWIFT_LEFT = 1;
        final static int SWIFT_TOP = 2;
        final static int SWIFT_RIGHT = 3;
        final static int SWIFT_BOTTOM = 4;

        public void createPopForKey(NineKeyButton keyButton,int swiftMode,boolean swift) {
            if(longPressView.getContentDescription().equals("KeyBoard_Function") || longPressView.repetable)
                return;
            BalloonHint hint = mSkbContainer.mBalloonPopup;
            String lable = "";
            switch (swiftMode)
            {
                case SWIFT_CENTER:
                    lable = keyButton.getUpperText();
                    if(lable == null || lable.length() == 0)
                    {
                        createPopForKey(keyButton,SWIFT_TOP,swift);
                        return;
                    }
                    break;
                case SWIFT_LEFT:
                    if(keyButton.getText().length() > 3)
                        lable += keyButton.getText().charAt(3);
                    break;
                case SWIFT_TOP:
                    if(keyButton.getText().length() > 0) {
                        if(swift)
                        {
                            lable += keyButton.getUpperText();
                            if(lable == null || lable.length() == 0)
                            {
                                createPopForKey(keyButton,SWIFT_TOP,swift);
                                return;
                            }
                        }else
                        {
                            lable += keyButton.getText().charAt(0);
                        }
                    }
                    break;
                case SWIFT_RIGHT:
                    if(keyButton.getText().length() > 1)
                        lable += keyButton.getText().charAt(1);
                    break;
                case SWIFT_BOTTOM:
                    if(keyButton.getText().length() > 2)
                        lable += keyButton.getText().charAt(2);
                    break;
            }
            if(lable == null || lable.length() == 0){
                return;
            }
            hint.setParent(keyButton);
            hint.setBalloonConfig(lable,keyButton.getTextSize()*2,false,
                    getResources().getColor(R.color.active_popup_color),keyButton.getWidth(),keyButton.getHeight(),20);
            hint.setBalloonBackground(getResources().getDrawable(R.drawable.popup_background));
            hint.delayedShow(0,new int[]{0,-keyButton.getHeight()-(int)(keyButton.getHeight()/70.0f*12.0f)});
        }

        public void run() {
            mTimerRuning = true;
            if (mWaitForTouchUp) {
                if(longPressView.repetable)
                {
                    responseKeyEvent(longPressView);
                    postAtTime(this, SystemClock.uptimeMillis() + LONG_PRESS_TIMEOUT2);
                }else
                {
                    isSwift = false;
                    createPopForKey(longPressView,mSwiftMode,isSwift);
                }
            }
        }
    }
}
